package limiter

import (
	"sync"
	"time"
)

// RateLimiter 限流器接口
type RateLimiter interface {
	// Allow 判断是否允许请求通过
	Allow() bool
}

// TokenBucket 令牌桶限流器
type TokenBucket struct {
	rate         float64    // 令牌生成速率（个/秒）
	capacity     int        // 桶容量
	tokens       float64    // 当前令牌数
	lastTokenSec float64    // 上次生成令牌的时间戳（秒）
	mutex        sync.Mutex // 互斥锁
}

// NewTokenBucket 创建一个令牌桶限流器
func NewTokenBucket(rate float64, capacity int) *TokenBucket {
	// 确保容量至少为1，否则令牌桶没有意义
	if capacity < 0 {
		capacity = 0
	}

	return &TokenBucket{
		rate:         rate,
		capacity:     capacity,
		tokens:       float64(capacity),
		lastTokenSec: nowInSeconds(),
	}
}

// 获取当前时间（秒），使用小数部分表示毫秒
func nowInSeconds() float64 {
	return float64(time.Now().UnixNano()) / float64(time.Second)
}

// Allow 判断是否允许请求通过
func (tb *TokenBucket) Allow() bool {
	tb.mutex.Lock()
	defer tb.mutex.Unlock()

	// 计算当前时间与上次生成令牌的时间差
	now := nowInSeconds()
	elapsed := now - tb.lastTokenSec

	// 生成新令牌
	tb.lastTokenSec = now
	tb.tokens = tb.tokens + elapsed*tb.rate

	// 令牌数不能超过容量
	if tb.capacity > 0 && tb.tokens > float64(tb.capacity) {
		tb.tokens = float64(tb.capacity)
	}

	// 判断是否有足够的令牌
	if tb.tokens < 1 {
		return false
	}

	// 消耗一个令牌
	tb.tokens--
	return true
}

// LeakyBucket 漏桶限流器
type LeakyBucket struct {
	rate       float64    // 漏出速率（个/秒）
	capacity   int        // 桶容量
	water      int        // 当前水量
	lastLeakMS int64      // 上次漏水时间戳（毫秒）
	mutex      sync.Mutex // 互斥锁
}

// NewLeakyBucket 创建一个漏桶限流器
func NewLeakyBucket(rate float64, capacity int) *LeakyBucket {
	return &LeakyBucket{
		rate:       rate,
		capacity:   capacity,
		water:      0,
		lastLeakMS: time.Now().UnixNano() / int64(time.Millisecond),
	}
}

// Allow 判断是否允许请求通过
func (lb *LeakyBucket) Allow() bool {
	lb.mutex.Lock()
	defer lb.mutex.Unlock()

	// 计算当前时间与上次漏水时间的时间差（毫秒）
	now := time.Now().UnixNano() / int64(time.Millisecond)
	elapsed := now - lb.lastLeakMS
	lb.lastLeakMS = now

	// 计算这段时间内漏出的水量
	leaked := int(float64(elapsed) * lb.rate / 1000)

	// 更新水量
	lb.water = lb.water - leaked
	if lb.water < 0 {
		lb.water = 0
	}

	// 判断桶是否已满
	if lb.water >= lb.capacity {
		return false
	}

	// 放入一滴水
	lb.water++
	return true
}

// SlidingWindow 滑动窗口限流器
type SlidingWindow struct {
	rate     int           // 窗口内最大请求数
	window   time.Duration // 窗口大小
	requests []int64       // 请求时间戳记录
	mutex    sync.Mutex    // 互斥锁
}

// NewSlidingWindow 创建一个滑动窗口限流器
func NewSlidingWindow(rate int, window time.Duration) *SlidingWindow {
	return &SlidingWindow{
		rate:     rate,
		window:   window,
		requests: make([]int64, 0, rate),
	}
}

// Allow 判断是否允许请求通过
func (sw *SlidingWindow) Allow() bool {
	sw.mutex.Lock()
	defer sw.mutex.Unlock()

	now := time.Now().UnixNano()
	threshold := now - sw.window.Nanoseconds()

	// 移除窗口外的请求记录
	valid := 0
	for i, ts := range sw.requests {
		if ts >= threshold {
			valid = i
			break
		}
	}

	sw.requests = sw.requests[valid:]

	// 判断是否超过速率限制
	if len(sw.requests) >= sw.rate {
		return false
	}

	// 记录当前请求
	sw.requests = append(sw.requests, now)
	return true
}
